package dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

abstract class GenericDAO<T> implements Serializable {
    private static final long serialVersionUID = 1L;
 
    private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("prjFinal");
    private EntityManager em;
 
    private Class<T> entityClass;
 
    public void beginTransaction() {
        em = emf.createEntityManager();
 
        em.getTransaction().begin();
    }
 
    public void commit() {
        em.getTransaction().commit();
    }
 
    public void rollback() {
        em.getTransaction().rollback();
    }
 
    public void closeTransaction() {
        em.close();
    }
 
    public void commitAndCloseTransaction() {
        commit();
        closeTransaction();
    }
 
    public void flush() {
        em.flush();
    }
 
    public void joinTransaction() {
        em = emf.createEntityManager();
        em.joinTransaction();
    }
 
    public GenericDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }
 
    public void save(T entity) {
        em.persist(entity);
    }
 
    public void delete(T entity) {
        T entityToBeRemoved = em.merge(entity);
 
        em.remove(entityToBeRemoved);
    }
 
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findProjetcs(int startingAt, int maxPerPage) {
       
        // regular query that will search for players in the db
    	 CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
         cq.select(cq.from(entityClass));
         em.createQuery(cq).setFirstResult(startingAt);
         em.createQuery(cq).setMaxResults(maxPerPage);
         
        return em.createQuery(cq).getResultList();
    }    
    
    public int countTestSuiteTotal() {
        
        Query query = em.createQuery("select COUNT(p) from TestSuite p");
 
        Number result = (Number) query.getSingleResult();
 
        return result.intValue();
    }
    
    public T update(T entity) {
        return em.merge(entity);
    }
 
    public T find(int entityID) {
        return em.find(entityClass, entityID);
    }
 
    public T findReferenceOnly(int entityID) {
        return em.getReference(entityClass, entityID);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public List<T> findAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        
        return em.createQuery(cq).getResultList();
    }
    
    @SuppressWarnings({ "hiding", "unchecked" })
	public <T> List<T> getList(Class<T> classToCast, String query, Object... values) {
        Query qr = createQuery(query, values);
        return qr.getResultList();
    }
    
    @SuppressWarnings("unchecked")
    protected T findOneResult(String namedQuery, Map<String, Object> parameters) {
        T result = null;
 
        try {
            Query query = em.createNamedQuery(namedQuery);
 
            // Method that will populate parameters if they are passed not null and empty
            if (parameters != null && !parameters.isEmpty()) {
                populateQueryParameters(query, parameters);
            }
 
            result = (T) query.getSingleResult();
 
        } catch (NoResultException e) {
            System.out.println("No result found for named query: " + namedQuery);
        } catch (Exception e) {
            System.out.println("Error while running query: " + e.getMessage());
            e.printStackTrace();
        }
 
        return result;
    }
 
    private void populateQueryParameters(Query query, Map<String, Object> parameters) {
        for (Entry<String, Object> entry : parameters.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
    }
    private Query createQuery(String query, Object[] values) {
        Query qr = em.createQuery(query);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                Object object = values[i];
                qr.setParameter(i + 1, object);
            }
        }
        return qr;
    }
}
